package org.jpro.common.jpa.repository;

import static org.springframework.data.jpa.repository.query.QueryUtils.applyAndBind;
import static org.springframework.data.jpa.repository.query.QueryUtils.getQueryString;

import java.io.Serializable;
import java.sql.Timestamp;
import java.util.Date;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.LockModeType;

import org.jpro.common.bean.BaseEntity;
import org.jpro.modules.system.realm.AuthorizingRealm;
import org.jpro.modules.system.utils.UserUtil;
import org.springframework.data.jpa.repository.support.JpaEntityInformation;
import org.springframework.data.jpa.repository.support.JpaEntityInformationSupport;
import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.util.Assert;

import com.google.common.collect.Lists;

import lombok.extern.slf4j.Slf4j;

/**
 * <P>功能描述 : 重写org.springframework.data.jpa.repository.support.SimpleJpaRepository
 * 实现类，使用自己的实现工厂，这样删除的操作可以做逻辑删除等等， 扩展一些自己的方法 </P>
 * <P>File name : BaseRepositoryImpl </P>
 * <P>Author : 王泽浩 </P>
 * <P>E-Mail : 1028625100@qq.com </P>
 * <P>Date : 2017/3/4 下午2:17</P>
 */
@Slf4j
@NoRepositoryBean
public class SimpleJpaRepository<T extends BaseEntity, ID extends Serializable>
        extends org.springframework.data.jpa.repository.support.SimpleJpaRepository<T, ID> implements JpaRepository<T, ID> {

    /**
     * 批量删除采用逻辑删除
     */
    public static final String DELETE_ALL_QUERY_STRING = "update %s x set x.del_flag = " + BaseEntity.DEL_FLAG_TRUE;

    /**
     * 持久化上下文
     */
    private final JpaEntityInformation<T, ?> entityInformation;
    private final EntityManager em;

    private final Class<T> entityClass;

    public SimpleJpaRepository(Class<T> domainClass, EntityManager em) {
        this(JpaEntityInformationSupport.getEntityInformation(domainClass, em), em);
    }

    /**
     * Creates a new {@link SimpleJpaRepository} to manage objects of the given
     * {@link JpaEntityInformation}.
     * @param entityInformation must not be {@literal null}.
     * @param entityManager must not be {@literal null}.
     */
    public SimpleJpaRepository(JpaEntityInformation<T, ?> entityInformation, EntityManager entityManager) {
        super(entityInformation, entityManager);
        this.entityInformation = entityInformation;
        this.em = entityManager;
        this.entityClass = entityInformation.getJavaType();
    }

    /**
     * <P>功能描述 : 保存 </P>
     * <P>Method Name : save </P>
     * <P>Author : 王泽浩 </P>
     * <P>E-Mail : 1028625100@qq.com </P>
     * <P>Date : 2017-03-04 16:44:12</P>
     * @param <S> 实体
     * @param entity 需要保存更新的实体
     * @return 返回保存后的实体
     * @since 1.0
     */
    @Override
    public <S extends T> S save(S entity) {
        this.updateBaseEntity(entity);
        return super.save(entity);
    }

    /**
     * (non-Javadoc)
     * @see org.springframework.data.jpa.repository.JpaRepository#deleteInBatch(java.lang.Iterable)
     */
    @Override
    public void deleteInBatch(Iterable<T> entities) {
        Assert.notNull(entities, "The given Iterable of entities not be null!");

        if (!entities.iterator().hasNext()) {
            return;
        }

        applyAndBind(getQueryString(DELETE_ALL_QUERY_STRING, entityInformation.getEntityName()), entities, em).executeUpdate();
    }

    /**
     * <P>功能描述 : 逻辑删除</P>
     * <P>Method Name : delete </P>
     * <P>Author : 王泽浩 </P>
     * <P>E-Mail : 1028625100@qq.com </P>
     * <P>Date : 2017-03-04 16:42:47</P>
     * @param t 实体
     * @since 1.0
     */
    @Override
    public void delete(T t) {
        t.del();
        this.save(t);
    }

    /**
     * <P>功能描述 : 在更新数据的时候更新操作人和操作时间 </P>
     * <P>Method Name : updateBaseEntity </P>
     * <P>Author : 王泽浩 </P>
     * <P>E-Mail : 1028625100@qq.com </P>
     * <P>Date : 2017-03-04 16:42:12</P>
     * @param entity the entity
     * @since 1.0
     */
    protected void updateBaseEntity(T entity) {
        AuthorizingRealm.ShiroUser currentShiroUser = UserUtil.getCurrentShiroUser();
        String name = "";
        if (currentShiroUser != null) {
            name = currentShiroUser.getName();
        }
        if (entityInformation.isNew(entity)) {
            entity.setCreateBy(name);
            entity.setCreateDate(new Timestamp(new Date().getTime()));
        } else {
            entity.setUpdateBy(name);
            entity.setUpdateDate(new Timestamp(new Date().getTime()));
        }
    }

    @Override
    public T findOne(ID id, LockModeType lockModeType) {
        if (lockModeType == null) {
            return super.findOne(id);
        } else {
            return em.find(entityClass, id, lockModeType);
        }
    }

    @Override
    public void deleteByIds(Iterable<ID> ids) {

        List<BaseEntity> entities = Lists.newArrayList();

        for (ID id : ids) {
            entities.add(new BaseEntity(id));
        }

        Assert.notNull(entities, "The given Iterable of entities not be null!");

        if (!entities.iterator().hasNext()) {
            return;
        }

        applyAndBind(getQueryString(DELETE_ALL_QUERY_STRING, entityInformation.getEntityName()), entities, em).executeUpdate();

    }

    @Override
    public void lock(T t, LockModeType lockModeType) {
        if (t != null && lockModeType != null) {
            em.lock(t, lockModeType);
        }
    }

    @Override
    public void clear() {
        em.clear();
    }

}
